/*******************************************************************************
 * CopyRight (c) 2020 
 *
 * \file    main.c
 * \brief   demux mp4 and mkv format, output video and audio stream to file or buffer
 * \author  ZhangJing
 * \version 1.0.0
 * \date    2020-06-22
 *
 * -----------------------------------------------------------------------------
 * Modified history：
 * <Date>       | <Version> | <Author>  | <Describe>
 * 2020-06-22   | v1.0.0    | ZhangJing | Create file
 * -----------------------------------------------------------------------------
 ******************************************************************************/

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#include "publicdef.h"
#include "print_ctrl.h"
#include "format.h"

int use_file_or_buffer = 0;         /**< 0 use file, 1 use buffer */
FormatContext *input_file = NULL;
static const char *filename = NULL; /**< input file name */

int nb_output_streams = 0;
FormatContext **output_streams = NULL;

Format *format_list[] = {
    &format_mp4,
    &format_mkv
};

/*!
 * \brief free memory
 */
static int exit_program(void)
{
    int i;
    FormatContext *of;
    Stream *sm;

    if (input_file) {
        /** close file */
        if (input_file->io->fh)
            input_file->io->io_close(input_file->io);
        if (input_file->probe_data)
            free(input_file->probe_data);
        if (input_file->streams) {
            for (i = 0; i < input_file->nb_streams; i++) {
                sm = input_file->streams[i];
                if (sm->mp4st)
                    free(sm->mp4st);
//                if (sm->mkvst)
//                    free(sm->mkvst);
            }
            free(input_file->streams);
        }
        free(input_file);
    }

    if (output_streams) {
        for (i = 0; i < nb_output_streams; i++) {
            of = output_streams[i];
            /** close files */
            //BUG:
            //if (of->io->fh)
            //    of->io->io_close(of->io);
            //TODO:
        }
        //TODO: free output_streams[i]
        free(output_streams);
    }
}

/*!
 * \brief get input file or buffer infomation
 */
static int input_file_init() 
{
    char *probe_data;
    IOContext *io;
    int ret;

    /** malloc file struct */
    input_file = malloc(sizeof(FormatContext));
    if (!input_file)
        return -EPERM;
    memset(input_file, 0, sizeof(FormatContext));

    if (use_file_or_buffer) /* if use buffer input */
        input_file->io = &io_buffer;
    else /* if use file */
        input_file->io = &io_file;

    /** malloc format probe data */
    input_file->probe_data_len = PROBE_DATA_MAXSIZE;
    probe_data = malloc(input_file->probe_data_len);
    if (!probe_data)
        return -EPERM;
    input_file->probe_data = probe_data;

    /** file open */
    io = input_file->io;
    ret = io->io_open(io, filename, "r");
    if (ret)
        return ret;

    /** get probe data */
    ret = io->io_read(io, input_file->probe_data, input_file->probe_data_len);
    if (ret > 0)
        return 0;

    return -EPERM;
}

/*!
 * \brief format probe, mp4 and mkv only now
 */
static int probe_format(void)
{
    int i;
    int ret = -EILSEQ;
    int num = sizeof(format_list) / sizeof(format_list[0]);
    FormatContext *fc = input_file;
    Format *fm1;

    pr_entry(in);

    for (i = 0; i < num; i++) {
        fm1 = format_list[i];
        if (fm1 && fm1->read_probe) {
            ret = fm1->read_probe(fc);
            if (!ret) {
                pr_info("The format type is %s", fm1->formatname);
                fc->fm = fm1;
                return ret;
            }
        }
    }

    return ret;
}

/*!
 * \brief get stream info from format file
 *        mp3, aac, h264, hevc support now
 */
static int get_stream_info()
{
    FormatContext *fc = input_file;
    Format *fm = fc->fm;

    return fm->read_header(fc);
}

/*!
 * \brief output stream file from streams array
 */
static int stream_output(void)
{
    FormatContext *ifc = input_file;
    int i;
    FormatContext *ofile;
    Stream *st;
    unsigned char oname[64];
    IOContext *of;

    nb_output_streams = ifc->nb_streams;
    //BUG: the malloc have bug???
    //output_streams = malloc(nb_output_streams * sizeof(FormatContext));
    output_streams = malloc(nb_output_streams * sizeof(*output_streams));
    for (i = 0; i < nb_output_streams; i++) {
        output_streams[i] = malloc(sizeof(FormatContext));
    }

    //FIXME: add video and audio parser thread
    for (i = 0; i < nb_output_streams; i++) {
        ofile = output_streams[i];
        st = ifc->streams[i];
        strcpy(oname, "output.");
        if (st->streamname) {
            pr_info("stream name: %s", st->streamname);
        } else {
            continue;
        }

        strcat(oname, st->streamname);
        ofile->ofname = oname; //FIXME:
        pr_info("output file: %s\n", oname);

        /** malloc output file struct */
#if 0 //BUG: the output file and input file handle conflict
        if (use_file_or_buffer) /* if use buffer input */
            ofile->io = &io_buffer;
        else /* if use file */
            ofile->io = &io_file;
        ofile->io_in = ifc->io;
#endif

        ofile->io = ifc->io; //FIXME:

        /** get output param */
        ofile->fm = ifc->fm;
        ofile->nb_streams = 1;
        ofile->streams = &ifc->streams[i];
        st = ofile->streams[0];
        if (ofile->fm->id == ID_MP4)
            st->ofm = &mp4_output_format;
        if (ofile->fm->id == ID_MKV)
            st->ofm = &mkv_output_format;
        if(st->ofm)
            pr_info("output format name: %s", st->ofm->output_format_name);

        if (st->id == ID_H264)
            st->bsf = &h264_mp4toannexb_bsf;
        else if (st->id == ID_HEVC) {
            st->bsf = &hevc_mp4toannexb_bsf;
        } else if (st->id == ID_AAC)
            st->bsf = &adts_bsf;
        if (st->bsf)
            pr_info("bsf name: %s", st->bsf->bsfname);

        /** create output file */
        of = ofile->io;
        //BUG:
        //of->io_open(of, oname, "w+");

        /** output file */
        if (st->ofm->write_header)
            st->ofm->write_header(ofile);
        if (st->ofm->write_packet)
            st->ofm->write_packet(ofile);
        if (st->ofm->write_trailer)
            st->ofm->write_trailer(ofile);
    }

    return 0;
}

/*!
 * \brief     the entry of program
 *
 * \param[in] argc param count
 * \param[in] argv parameters, the first param is program name,
 *                 the second param is video file name
 * \return    0 if succeeded otherwise failed
 */
int main(int argc, char *argv[])
{
    int ret;

    pr_info("%s start...", argv[0]);

    /** get filename from argv */
    if (argc < 2) {
        pr_err("Please use %s filename", argv[0]);
        return 0;
    }
    if (argc > 3) {
        pr_err("The param %s... not support now", argv[2]);
    }
    filename = argv[1];
    pr_info("Input filename: %s", filename);

    /** get file info, open file, and get probe data */
    ret = input_file_init();
    if (ret)
        goto exit;

    /** probe the format of file */
    ret = probe_format();
    if (ret < 0)
        goto exit;

    /** get stream from format file */
    ret = get_stream_info();
    if (ret < 0)
        goto exit;

    /** stream output */
    ret = stream_output();
    if (ret < 0)
        goto exit;

exit:
    /** free memory and close file */
    exit_program();

    return 0;
}
